package myJdkProxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 手写一个简单的生成代理类的方法
 *     private UserService userService;
 *
 *     public LogProxy(UserService target){
 *         this.userService = target;
 *     }
 */
public class ProxyUtil {

    public static Object newProxyInstance(Object target) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        String content = "";
        String packageContent = "package myJdkProxy;";

        Class targetInfo = target.getClass().getInterfaces()[0];
        String targetInfoName = targetInfo.getSimpleName();
        String importContent = "import "+targetInfo.getName()+";";


        String classContent = "public class $Proxy implements "+targetInfoName+"{";

        String fieldContent = "private "+targetInfoName+" target;";

        String construterContent = "public $Proxy("+targetInfoName+"  target){"
                +"this.target = target;}";

        String methodsContent = "";
        Method[]  methods = targetInfo.getDeclaredMethods();
        for(Method method : methods){
            String methodName = method.getName();
            Class returnType = method.getReturnType();
            Class[] parameterTypes = method.getParameterTypes();
            String argsContents = "";
            String argsNames = "";
            int i = 0;
            for(Class parameterType : parameterTypes){
                String simpName = parameterType.getSimpleName();
                argsContents+= simpName + " p"+i;
                argsNames+="p"+i+",";
                i++;
            }
            if(argsContents.length()>0){
                argsContents = argsContents.substring(0,argsContents.lastIndexOf(",")-1);
                argsNames = argsNames.substring(0,argsContents.lastIndexOf(",")-1);
            }
            methodsContent = "public "+returnType+" "+methodName+"("+argsContents+"){"+
                    "System.out.println(\"log...\");"+
                    "target."+methodName+"("+argsNames+");}";
        }
        content += packageContent+importContent+classContent+fieldContent+construterContent+methodsContent+"}";

        File file = new File("d:\\ProxyTest\\$Proxy.java");
        if(!file.exists()){
            file.createNewFile();
        }
        FileWriter  fileWriter = new FileWriter(file);
        fileWriter.write(content);
        fileWriter.flush();
        fileWriter.close();

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null,null,null);
        Iterable units = fileManager.getJavaFileObjects(file);
        JavaCompiler.CompilationTask task = compiler.getTask(null,fileManager,null,null,null,units);
        task.call();
        fileManager.close();

        URL[] urls = new URL[]{new URL("file:d:\\\\")};
        URLClassLoader urlClassLoader = new URLClassLoader(urls);
        Class clazz = urlClassLoader.loadClass("myJdkProxy.$Proxy");

        Constructor constructor = clazz.getConstructor(targetInfo);
        constructor.newInstance(target);
        Object o = clazz.newInstance();

        return o;
    }
}
